package dao.Impl;

import dao.BaseDao;
import util.DBUtils;

import java.lang.reflect.*;
import java.math.BigDecimal;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class BaseDaoImpl<T> implements BaseDao<T> {
    private Class clazz;

    private static <T> T newTclass(Class<T> clazz) throws InstantiationException, IllegalAccessException {
        T a = clazz.newInstance();
        return a;
    }

    public BaseDaoImpl() {
        //1.得到当前运行类的class对象
        Class cs = this.getClass();
        //2.得到运行类的父类的参数化类型
        Type type = cs.getGenericSuperclass();
        //把ptype转换成Type的子接口类型
        if (type instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType) type;
            //3.得到实际类型参数
            Type[] types = ptype.getActualTypeArguments();
            //4.把types变成其实现类Class
            Class classImpl = (Class) types[0];
            this.clazz = classImpl;
        }
    }

    public <T> T getNewObject(Class<T> cls) {
        T t = null;
        try {
            t = cls.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return t;
    }

    @Override
    public void insert(T t, Connection conn) throws SQLException {
        Class<?> class1 = t.getClass();
        String tableName = class1.getSimpleName();
        String getName = null;
        Field[] df = class1.getDeclaredFields();
        String sql = "insert into " + tableName + "(";
        for (int i = 0; i < df.length; i++) {
            Field field = df[i];
            if (i < df.length - 1) {
                sql += field.getName() + ",";
            } else {
                sql += field.getName() + ") values(";
            }
        }
        for (int i = 0; i < df.length; i++) {
            if (i < df.length - 1) {
                sql += "?,";
            } else {
                sql += "?)";
            }
        }
        PreparedStatement pstm = conn.prepareStatement(sql);
        try {
            df[0].setAccessible(true);
            pstm.setObject(1,null);
            for (int i = 1; i < df.length; i++) {
                df[i].setAccessible(true);
                pstm.setObject(i + 1, df[i].get(t));
            }
            int i = pstm.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        DBUtils.close(null, pstm, conn);

    }


    @Override
    public void delete(T t, Connection conn) throws SQLException {
        Class<?> class1 = t.getClass();
        String tableName = class1.getSimpleName();
        Field[] df = class1.getDeclaredFields();
        String getName = null;
        String sql = "delete from " + tableName + " where ";
        Field field = df[0];
        getName = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
        sql += field.getName() + "=?";
        PreparedStatement pstm = conn.prepareStatement(sql);
        try {
            pstm.setObject(1, class1.getDeclaredMethod(getName).invoke(t));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        int i = pstm.executeUpdate();
        DBUtils.close(null, pstm, conn);
    }

    @Override
    public void update(T t, Connection conn) throws SQLException {
        Class<?> class1 = t.getClass();
        String tableName = class1.getSimpleName();
        String getName = null;
        Field[] df = class1.getDeclaredFields();
        String sql = "update " + tableName + " set ";
        for (int i = 0; i < df.length; i++) {
            Field field = df[i];
            getName = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
            sql += field.getName() + "=?";
            if (i < df.length - 1) {
                sql += ",";
            } else {
                sql += " where id=?";
            }
        }
        PreparedStatement ps = conn.prepareStatement(sql);
        String where;
        for (int i = 0; i < df.length; i++) {
            Field field = df[i];
            getName = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
            try {
                if (i == 0) {
                    where = getName;
                    ps.setObject(df.length + 1, class1.getDeclaredMethod(where).invoke(t));

                }
                Method method = class1.getDeclaredMethod(getName);
                ps.setObject(i + 1, method.invoke(t));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            }

        }
        int executeUpdate = ps.executeUpdate();
        DBUtils.close(null, ps, conn);
    }

    @Override
    public List<T> queryMany(String sql, Class<T> cls, Object... args) {
        Connection conn = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        List<T> list = new ArrayList<T>();
        try {
            conn = DBUtils.getConnection();
            pst = conn.prepareStatement(sql);
            if(args==null){
                rs = pst.executeQuery();
                ResultSetMetaData metaData = rs.getMetaData();
                while (rs.next()) {
                    T obj = cls.newInstance();
                    for (int i = 1; i <= metaData.getColumnCount(); i++) {
                        String columnLabel = metaData.getColumnLabel(i);
                        String name = "set" + columnLabel.substring(0, 1).toUpperCase() + columnLabel.substring(1);
                        Field field = cls.getDeclaredField(columnLabel);
                        Method method = cls.getDeclaredMethod(name, field.getType());
                        Object realParam = rs.getObject(columnLabel);
                        method.invoke(obj, realParam);
                    }
                    list.add(obj);
                }

            }else{
                for (int i = 0; i < args.length; i++) {
                    pst.setObject(i + 1, args[i]);
                }
                rs = pst.executeQuery();
                ResultSetMetaData metaData = rs.getMetaData();
                while (rs.next()) {
                    T obj = cls.newInstance();
                    for (int i = 1; i <= metaData.getColumnCount(); i++) {
                        String columnLabel = metaData.getColumnLabel(i);
                        String name = "set" + columnLabel.substring(0, 1).toUpperCase() + columnLabel.substring(1);
                        Field field = cls.getDeclaredField(columnLabel);
                        Method method = cls.getDeclaredMethod(name, field.getType());
                        Object realParam = rs.getObject(columnLabel);
                        method.invoke(obj, realParam);
                    }
                    list.add(obj);
                }
            }

        } catch (SQLException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } finally {
            DBUtils.close(rs, pst, conn);
        }
        return list;
    }

    @Override
    public T queryOne(String sql, Class<T> cls, Object... args) {
        List<T> list = this.queryMany(sql, cls, args);
        return list.isEmpty() ? null : list.get(0);
    }
}